home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / FUZZY.ZIP / AVL.B next >
Text File  |  1986-11-30  |  16KB  |  502 lines

  1. -------------------------------------------------------------------------------
  2. --                                                                           --
  3. --  Library Unit:  AVL  --  Generic AVL tree package                         --
  4. --                                                                           --
  5. --  Author:  Bradley L. Richards                                             --
  6. --                                                                           --
  7. --     Version     Date     Notes . . .                                      --
  8. --    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    --
  9. --       1.0    12 Mar 86   Initial Version (delete & update not done)       --
  10. --       1.1    19 Aug 86   Added update and release procedures              --
  11. --       1.2     7 Sep 86   Added delete procedure; cleaned up code          --
  12. --    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    --
  13. --                                                                           --
  14. --  Library units used:  none                                                --
  15. --                                                                           --
  16. --  Description:  This package provides generic functions for creating,      --
  17. --      modifying, and accessing AVL trees.  AVL trees are binary trees      --
  18. --      which never have more than one level of imbalance between any        --
  19. --      two subtrees.  Balance is maintained automatically when the tree     --
  20. --      is being built.                                                      --
  21. --           The data to be maintained in the tree is never actually passed  --
  22. --      to this package.  Rather, pointers to the data are passed in, via    --
  23. --      type "node_ptr."  Also, comparison functions on the key fields of    --
  24. --      the data must be provided.  The package requires a less-than and an  --
  25. --      equality test.                                                       --
  26. --                                                                           --
  27. -------------------------------------------------------------------------------
  28. package body avl is
  29.  
  30.   procedure add_node( tree : in out tree_ptr; data : in node_ptr;
  31.                              duplicate : out boolean) is
  32.       needs_balanced : boolean;
  33.       pivot_parent, pivot, pivot_child, pivot_grandchild : tree_ptr := null;
  34.  
  35.       procedure insert_node( tree : in out tree_ptr; data : in node_ptr;
  36.                  duplicate, needs_balanced : out boolean;
  37.                  pivot_parent, pivot, pivot_child,
  38.                  pivot_grandchild : out tree_ptr ) is
  39.       found, pivot_found, placed : boolean := false;
  40.       ptr_child, ptr_grandchild : tree_ptr := null;
  41.       ptr : tree_ptr := tree;
  42.     begin
  43.       if tree = null then -- no nodes in tree
  44.         tree := new tree_node'(same, null, null, null, data);
  45.         duplicate := false;
  46.         needs_balanced := false;
  47.       else -- must search tree
  48.         loop
  49.           if equal(data, ptr.data) then
  50.         found := true;
  51.           elsif less_than(data, ptr.data) then
  52.         if ptr.left_child = null then
  53.           ptr.left_child := new tree_node'(same,null,null,ptr,data);
  54.           ptr_child := ptr.left_child;
  55.           placed := true;
  56.         else
  57.           ptr := ptr.left_child;
  58.         end if;
  59.           else
  60.         if ptr.right_child = null then
  61.           ptr.right_child := new tree_node'(same,null,null,ptr,data);
  62.           ptr_child := ptr.right_child;
  63.           placed := true;
  64.         else
  65.           ptr := ptr.right_child;
  66.         end if;
  67.           end if;
  68.           exit when found or placed;
  69.         end loop;
  70.         if found then
  71.           duplicate := true;
  72.         else -- trace back through the tree adjusting balances
  73.           duplicate := false;
  74.           loop
  75.         case ptr.balance is
  76.           when left      => if ptr_child = ptr.left_child then
  77.                       ptr.balance := tall_left;
  78.                     else
  79.                       ptr.balance := same;
  80.                     end if;
  81.                     pivot_found := true;
  82.           when same      => if ptr_child = ptr.left_child then
  83.                       ptr.balance := left;
  84.                     else
  85.                       ptr.balance := right;
  86.                     end if;
  87.                     ptr_grandchild := ptr_child;
  88.                     ptr_child := ptr;
  89.                     ptr := ptr.parent;
  90.           when right     => if ptr_child = ptr.left_child then
  91.                       ptr.balance := same;
  92.                     else
  93.                       ptr.balance := tall_right;
  94.                     end if;
  95.                     pivot_found := true;
  96.           when others    => -- some sort of major tree construction
  97.                     -- error has occurred
  98.                     raise avl_error;
  99.           end case;
  100.         exit when pivot_found or (ptr = null);
  101.           end loop;
  102.           needs_balanced := false;
  103.           if pivot_found then
  104.         if (ptr.balance = tall_left) or (ptr.balance = tall_right) then
  105.           needs_balanced := true;
  106.           pivot_parent := ptr.parent;
  107.           pivot := ptr;
  108.           pivot_child := ptr_child;
  109.           pivot_grandchild := ptr_grandchild;
  110.         end if;
  111.           end if;
  112.         end if;
  113.       end if;
  114.     end insert_node;
  115.  
  116.  
  117.     begin -- add node
  118.       --
  119.       --  insert_node places the node into the tree, adjusts all
  120.       --  required balances, and determines whether or not the
  121.       --  tree needs balanced.  If it does, pivot points to the
  122.       --  pivot node for the rotation(s)
  123.       --
  124.       insert_node(tree, data, duplicate, needs_balanced, pivot_parent,
  125.           pivot, pivot_child, pivot_grandchild);
  126.       if needs_balanced then
  127.     if needs_single_rotation(pivot_parent, pivot, pivot_child,
  128.                               pivot_grandchild) then
  129.       rotate_singly(pivot_parent, pivot, pivot_child);
  130.     else
  131.       rotate_doubly(pivot_parent, pivot, pivot_child, pivot_grandchild);
  132.     end if;
  133.     if pivot_parent = null then -- pivot points to new root node
  134.       tree := pivot;
  135.     end if;
  136.       end if;
  137.     end add_node;
  138.  
  139.  
  140.   function copy_tree( original : tree_ptr ) return tree_ptr is
  141.       root : tree_ptr;
  142.     begin
  143.       if original = null then
  144.     return null;
  145.       else
  146.     root := new tree_node;
  147.     root.balance := original.balance;
  148.     root.data := original.data;
  149.     root.left_child := copy_tree(original.left_child);
  150.     root.right_child := copy_tree(original.right_child);
  151.       end if;
  152.     end copy_tree;
  153.  
  154.   
  155.   --
  156.   --  Delete_node -- This routine, when implemented, will remove the matching
  157.   --                 node from the AVL structure and automatically rebalance
  158.   --                 the tree.  It should also allow an option to deallocate
  159.   --                 the data.
  160.   --
  161.   procedure delete_node( tree : in out tree_ptr; data : in node_ptr;
  162.                         not_found : out boolean) is
  163.       duplicate : boolean;
  164.       new_tree, parent, ptr : tree_ptr;
  165.  
  166.       --
  167.       --  Merge merges two trees together.  The right tree is assumed to be
  168.       --  either the smaller tree (for efficiency) or perhaps an invalid
  169.       --  AVL tree.
  170.       --
  171.       procedure merge( t1, t2 : tree_ptr; new_tree : out tree_ptr ) is
  172.       tree : tree_ptr := t1; -- t1 is the working AVL tree
  173.       ptr : tree_ptr := t2;
  174.       parent : tree_ptr;
  175.  
  176.     begin
  177.       if tree /= null then
  178.         tree.parent := null;
  179.       end if;
  180.       if ptr /= null then
  181.         ptr.parent := null;
  182.       end if;
  183.       while ptr /= null loop
  184.         if ptr.left_child /= null then
  185.           ptr := ptr.left_child;
  186.         elsif ptr.right_child /= null then
  187.           ptr := ptr.right_child;
  188.         else -- both children null
  189.           add_node(tree, ptr.data, duplicate);
  190.           if duplicate then
  191.             raise avl_error;
  192.           end if;
  193.           parent := ptr.parent;
  194.           if parent /= null then
  195.             if parent.left_child = ptr then
  196.               free_AVL(parent.left_child);
  197.             else
  198.               free_AVL(parent.right_child);
  199.             end if;
  200.           else
  201.         free_AVL(ptr);
  202.           end if;
  203.           ptr := parent;
  204.         end if;
  205.       end loop;
  206.       new_tree := tree;
  207.     end merge;
  208.  
  209.  
  210.     begin -- delete_node
  211.       if tree = null then
  212.     not_found := true;
  213.       else
  214.     ptr := fetch_node(tree, data);
  215.     if ptr = null then
  216.       not_found := true;
  217.     else
  218.       if ptr.balance = right then -- list taller tree first
  219.         merge(ptr.right_child, ptr.left_child, new_tree);
  220.       else
  221.         merge(ptr.left_child, ptr.right_child, new_tree);
  222.       end if;
  223.       parent := ptr.parent;
  224.       if parent /= null then -- didn't delete the root node
  225.         if parent.left_child = ptr then
  226.           parent.left_child := null;
  227.         else
  228.           parent.right_child := null;
  229.         end if;
  230.         merge(new_tree, tree, new_tree);
  231.       end if;
  232.       free_AVL(ptr);
  233.       tree := new_tree;
  234.     end if;
  235.       end if;
  236.     end delete_node;
  237.  
  238.  
  239.   --
  240.   --  Fetch_node -- This function returns a pointer to the data associated
  241.   --                with the AVL node which matches the input data key field.
  242.   --
  243.   function fetch_node( tree : tree_ptr; data : node_ptr) return node_ptr is
  244.       node : tree_ptr;
  245.     begin
  246.       node := fetch_node(tree, data);
  247.       if node = null then
  248.     return null;
  249.       else
  250.     return node.data;
  251.       end if;
  252.     end fetch_node;
  253.  
  254.   function fetch_node( tree : tree_ptr; data : node_ptr) return tree_ptr is
  255.       ptr : tree_ptr := tree;
  256.     begin
  257.       if tree = null then
  258.     return null;
  259.       else
  260.     loop
  261.       if equal(data, ptr.data) then
  262.         return ptr;
  263.       elsif less_than(data, ptr.data) then
  264.         if ptr.left_child = null then
  265.           return null;
  266.         else
  267.           ptr := ptr.left_child;
  268.         end if;
  269.       else
  270.         if ptr.right_child = null then
  271.           return null;
  272.         else
  273.           ptr := ptr.right_child;
  274.         end if;
  275.       end if;
  276.     end loop;
  277.       end if;
  278.     end fetch_node;
  279.  
  280.   
  281.   function init_tree return tree_ptr is
  282.     begin
  283.       return null;
  284.     end init_tree;
  285.  
  286.  
  287. function needs_single_rotation(p1, p2, p3, p4 : in tree_ptr) return boolean is
  288.   begin
  289.     if p4 /= null then
  290.       if ( (p3.balance = left) and (p2.balance = tall_right) ) or
  291.      ( (p3.balance = right) and (p2.balance = tall_left) ) then
  292.     return false; -- requires double rotation
  293.       else
  294.     return true;
  295.       end if;
  296.     else -- we shouldn't have been called
  297.       raise avl_error;
  298.     end if;
  299.   end needs_single_rotation;
  300.  
  301.  
  302. --procedure print_tree( tree : tree_ptr ) is -- debug
  303.  
  304.     --procedure print_node( node : tree_ptr; indent : natural ) is
  305.  
  306.     --procedure space( num : natural ) is
  307.       --begin
  308.         --for i in 1..num loop
  309.           --put(' ');
  310.         --end loop;
  311.       --end space;
  312.  
  313.       --begin
  314.     --space(indent);
  315.     --if node = null then
  316.       --put_line("<null>");
  317.     --else
  318.       --put_data(node.data);
  319.       --put("  ");
  320.       --put(node.balance);
  321.       --if (node.left_child /= null) and then
  322.          --(node.left_child.parent /= node) then
  323.         --put("  left child parent discrepancy");
  324.       --end if;
  325.       --if (node.right_child /= null) and then
  326.          --(node.right_child.parent /= node) then
  327.         --put("  right child parent discrepancy");
  328.       --end if;
  329.       --new_line;
  330.       --print_node(node.left_child, indent+2);
  331.       --print_node(node.right_child, indent+2);
  332.     --end if;
  333.       --end print_node;
  334.  
  335.   --begin
  336.     --if (tree /= null) and then (tree.parent /= null) then
  337.       --put_line("tree parent discrepancy");
  338.     --end if;
  339.     --print_node(tree, 0);
  340.   --end print_tree;
  341.  
  342.  
  343.   --
  344.   --  Release -- This routine releases all nodes in an AVL tree.  It does
  345.   --             not release the data associated with the nodes.  For cases
  346.   --             where the AVL structure was just a temporary way of
  347.   --             structuring the data this is fine, but eventually the
  348.   --             release procedure should allow an option to release data
  349.   --             associated with AVL nodes.  This will require another
  350.   --             generic procedure parameter to the package.
  351.   --
  352.   procedure release( tree : in out tree_ptr ) is
  353.     begin
  354.       if tree /= null then
  355.     if tree.left_child /= null then
  356.       release( tree.left_child );
  357.     end if;
  358.     if tree.right_child /= null then
  359.       release( tree.right_child );
  360.     end if;
  361.       free_AVL(tree);
  362.       end if;
  363.     end release;
  364.  
  365.  
  366. procedure rotate_doubly(p1, p2, p3, p4 : in out tree_ptr) is
  367.   begin
  368.     if p2.balance = tall_left then
  369.       p2.left_child := p4.right_child;
  370.       if p4.right_child /= null then
  371.     p4.right_child.parent := p2;
  372.       end if;
  373.       p3.right_child := p4.left_child;
  374.       if p4.left_child /= null then
  375.     p4.left_child.parent := p3;
  376.       end if;
  377.       p4.left_child := p3;
  378.       p4.right_child := p2;
  379.       case p4.balance is
  380.     when left   => p2.balance := right;
  381.                  p3.balance := same;
  382.     when same   => p2.balance := same;
  383.                p3.balance := same;
  384.       when right  => p2.balance := same;
  385.                p3.balance := left;
  386.     when others => raise avl_error;
  387.     end case;
  388.     else
  389.       p2.right_child := p4.left_child;
  390.       if p4.left_child /= null then
  391.     p4.left_child.parent := p2;
  392.       end if;
  393.       p3.left_child := p4.right_child;
  394.       if p4.right_child /= null then
  395.     p4.right_child.parent := p3;
  396.       end if;
  397.       p4.left_child := p2;
  398.       p4.right_child := p3;
  399.       case p4.balance is
  400.     when left   => p3.balance := right;
  401.                p2.balance := same;
  402.     when same   => p3.balance := same;
  403.                p2.balance := same;
  404.     when right  => p3.balance := same;
  405.                p2.balance := left;
  406.     when others => raise avl_error;
  407.     end case;
  408.     end if;
  409.     p4.parent := p1;
  410.     p4.balance := same;
  411.     p2.parent := p4;
  412.     p3.parent := p4;
  413.     if p1 = null then
  414.       p2 := p4;                                -- we've changed the root
  415.     elsif p1.left_child = p2 then
  416.       p1.left_child := p4;
  417.     else
  418.       p1.right_child := p4;
  419.     end if;
  420.   end rotate_doubly;
  421.  
  422.  
  423. procedure rotate_singly(p1, p2, p3 : in out tree_ptr) is
  424.   begin
  425.     if p2.balance = tall_left then
  426.       p2.left_child := p3.right_child;
  427.       if p3.right_child /= null then
  428.     p3.right_child.parent := p2;
  429.       end if;
  430.       p3.right_child := p2;
  431.       if p3.balance = left then
  432.     p2.balance := same;
  433.       else
  434.     p2.balance := left;
  435.       end if;
  436.     else
  437.       p2.right_child := p3.left_child;
  438.       if p3.left_child /= null then
  439.     p3.left_child.parent := p2;
  440.       end if;
  441.       p3.left_child := p2;
  442.       if p3.balance = right then
  443.     p2.balance := same;
  444.       else
  445.     p2.balance := right;
  446.       end if;
  447.     end if;
  448.     p2.parent := p3;
  449.     p3.balance := same;
  450.     p3.parent := p1;
  451.     if p1 = null then
  452.       p2 := p3;                           -- we've changed the root
  453.     elsif p1.left_child = p2 then
  454.       p1.left_child := p3;
  455.     else
  456.       p1.right_child := p3;
  457.     end if;
  458.   end rotate_singly;
  459.  
  460.  
  461.   --
  462.   --  Update_node -- This routine locates the node whose key field matches
  463.   --                 the data and replaces the node data witht the new data
  464.   --                 included in this call.  If no matching node is found
  465.   --                 not_found will be true.
  466.   --
  467.   --                 This routine should be modified to optionally release
  468.   --                 the old data.
  469.   --
  470.   procedure update_node( tree : in tree_ptr; data : in node_ptr;
  471.                         not_found : out boolean ) is
  472.       ptr : tree_ptr := tree;
  473.     begin
  474.       if tree = null then
  475.     not_found := true;
  476.       else
  477.     loop
  478.       if equal(data, ptr.data) then
  479.         not_found := false;
  480.         ptr.data := data;
  481.         exit;
  482.       elsif less_than(data, ptr.data) then
  483.         if ptr.left_child = null then
  484.           not_found := true;
  485.           exit;
  486.         else
  487.           ptr := ptr.left_child;
  488.         end if;
  489.       else
  490.         if ptr.right_child = null then
  491.           not_found := true;
  492.           exit;
  493.         else
  494.           ptr := ptr.right_child;
  495.         end if;
  496.       end if;
  497.     end loop;
  498.       end if;
  499.     end update_node;
  500.  
  501. end avl;
  502.